package com.zrkizzy.module.security.config;

import com.zrkizzy.module.security.OpenPolicyAgentAuthorizationManager;
import com.zrkizzy.module.security.filters.JwtAuthenticationTokenFilter;
import com.zrkizzy.module.security.response.LoginUserAccessDeniedHandler;
import com.zrkizzy.module.security.response.LoginUserAuthenticationEntryPoint;
import com.zrkizzy.common.security.enums.WhiteListEnum;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

/**
 * SpringSecurity配置类
 *
 * @author zhangrongkang
 * @since 2023/3/9
 */
@EnableWebSecurity
@Configuration
public class SecurityConfig {

    /**
     * 未登录或token失效返回结果
     */
    private final LoginUserAuthenticationEntryPoint authenticationEntryPoint;

    /**
     * 当访问接口没有权限时返回结果
     */
    private final LoginUserAccessDeniedHandler accessDeniedHandler;

    /**
     * 权限认证授权管理器
     */
    private final OpenPolicyAgentAuthorizationManager authorizationManager;

    /**
     * JWT登录授权过滤器
     */
    private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    public SecurityConfig(LoginUserAuthenticationEntryPoint authenticationEntryPoint, LoginUserAccessDeniedHandler accessDeniedHandler, OpenPolicyAgentAuthorizationManager authorizationManager, JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter) {
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.accessDeniedHandler = accessDeniedHandler;
        this.authorizationManager = authorizationManager;
        this.jwtAuthenticationTokenFilter = jwtAuthenticationTokenFilter;
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector)  throws Exception {
        MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
        // 基于token，不需要使用csrf
        http.csrf(AbstractHttpConfigurer::disable)
                // 开启无状态的会话管理 在无状态的情况下，服务器不会在会话中存储用户的状态信息，每个请求都必须包含足够的信息来理解和处理该请求
                .sessionManagement(sessionManagementConfigurer -> sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorizationRequests ->
                // 请求放行
                authorizationRequests
                        // 放行静态资源文件，确保使用Druid连接池后项目可以启动成功
                        .requestMatchers(mvcMatcherBuilder.pattern(HttpMethod.GET, "/**.html")).permitAll()
                        .requestMatchers(mvcMatcherBuilder.pattern(HttpMethod.GET, "/**.css")).permitAll()
                        .requestMatchers(mvcMatcherBuilder.pattern(HttpMethod.GET, "/**.js")).permitAll()
                        .requestMatchers(mvcMatcherBuilder.pattern(HttpMethod.GET, "/favicon.ico")).permitAll()
                        // 放行Druid请求路径
                        .requestMatchers(AntPathRequestMatcher.antMatcher("/druid/**")).permitAll()
                        .requestMatchers(AntPathRequestMatcher.antMatcher("/actuator/**")).permitAll()
                        // 放行公开请求
                        .requestMatchers(AntPathRequestMatcher.antMatcher(WhiteListEnum.COMMON.getUrl())).permitAll()
                        // 放行本地文件获取请求
                        .requestMatchers(AntPathRequestMatcher.antMatcher(WhiteListEnum.FILES.getUrl())).permitAll()
                        // 放行swagger相关文件
                        .requestMatchers(AntPathRequestMatcher.antMatcher(WhiteListEnum.SWAGGER_HOST.getUrl())).permitAll()
                        .requestMatchers(AntPathRequestMatcher.antMatcher(WhiteListEnum.SWAGGER_WEBJAR.getUrl())).permitAll()
                        .requestMatchers(AntPathRequestMatcher.antMatcher(WhiteListEnum.SWAGGER_RESOURCE.getUrl())).permitAll()
                        .requestMatchers(AntPathRequestMatcher.antMatcher(WhiteListEnum.SWAGGER_API.getUrl())).permitAll()
                        // 其余所有请求都需要进行自定义认证
                        .anyRequest().access(authorizationManager)
                )
                // 添加自定义请求过滤器，替代原生UsernamePasswordAuthenticationFilter
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                // 自定义异常
                .exceptionHandling(exceptions -> exceptions
                        // 未登录或Token失效
                        .authenticationEntryPoint(authenticationEntryPoint)
                        // 访问接口没有权限
                        .accessDeniedHandler(accessDeniedHandler))
                // 禁用默认登录页
                .formLogin(AbstractHttpConfigurer::disable)
                // 禁用默认登出页
                .logout(AbstractHttpConfigurer::disable);
        return http.build();
    }

}
